package com.ceair.flight.refund.service.audit.impl;

import com.ceair.flight.refund.dto.appmoblie.*;
import com.ceair.flight.refund.dto.audit.BaseAuditReq;
import com.ceair.flight.refund.dto.audit.FirstAuditReq;
import com.ceair.flight.refund.dto.audit.SegReq;
import com.ceair.flight.refund.dto.audit.TaxReq;
import com.ceair.flight.refund.dto.external.ServiceException;
import com.ceair.flight.refund.location.dao.IRefundApplyDao;
import com.ceair.flight.refund.location.dao.IRefundSegDao;
import com.ceair.flight.refund.location.dao.IRefundTaxDao;
import com.ceair.flight.refund.location.dao.IRefundTktDao;
import com.ceair.flight.refund.location.dt.enums.*;
import com.ceair.flight.refund.service.audit.IFirstAuditService;
import com.ceair.flight.refund.util.CurrencyUtil;
import com.ceair.flight.refund.util.GsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 *
 * @author youguilin
 *
 * 退票一审
 */
@Service("internalFirstAuditServiceImpl")
public class FirstAuditServiceImpl implements IFirstAuditService {
    private static final Logger LOG = LoggerFactory.getLogger(FirstAuditServiceImpl.class);

    @Autowired
    private IRefundApplyDao refundApplyDao;

    @Autowired
    private IRefundTktDao refundTktDao;

    @Autowired
    private IRefundSegDao refundSegDao;

    @Autowired
    private IRefundTaxDao refundTaxDao;
//    @Resource(name = "refundRejectNotifcation")
//    private IRefundNotifcation rejectNotication;

    @Override
    public void doPass(FirstAuditReq auditReq) throws ServiceException {
        long applyId = auditReq.getApplyId();
        LOG.info("Reund apply {} do pass first audit, the request is {}", applyId, auditReq);
        try {
            // 加载退票单,并且判断是否是一审的状态
            RefundApplyDto apply = this.loadApplyAndCheckStatus(applyId);
            // 退票审核
            saveAudit(apply, auditReq, AuditStatus.PASS);
            RefundTktDto refundTkt = apply.getRefundTkt();
            refundTkt.setCurrency(apply.getCurrency());
            refundTkt.setRescheduleFeeRes(auditReq.getRescheduleFeeRes());

            //update taxs
            updateRefundTax(auditReq, refundTkt);
            //update segments
            if (refundTkt.isInterAirline()) {
                updateRefundSegment4Inter(auditReq, refundTkt);
            } else {
                updateRefundSegment4Dome(auditReq, refundTkt);
            }
            //update ticket detail
            updateRefundTicket(auditReq, refundTkt);
            //update refund apply
            updateRefundApply(auditReq, apply);
        } catch (Exception ex) {
            LOG.error("Refund apply {} do first audit pass failed", applyId, ex);
            throw new IllegalStateException("Refund apply " + applyId + " do first audit failed, reason: " + ex.getMessage());
        }
    }

    @Override
    public void doRefuse(BaseAuditReq auditReq) throws ServiceException {
        // 退票单 判断是否是一审的状态
        RefundApplyDto apply = this.loadApplyAndCheckStatus(auditReq.getApplyId());
        LOG.info("Refund apply {} is rejected by {}", auditReq.getApplyId(), auditReq.getAuditName());
        // 设置等待用户更新
        apply.setStatus(RefundStatusEnum.WAIT_USER_UPDATE.getCode());
        apply.setUpdatDate(new Date());
        // 退票原因
        apply.setClaimReason(auditReq.getReason().getCode());
        refundApplyDao.updateRefundApply(apply);
        // 退票审核
        saveAudit(apply, auditReq, AuditStatus.REFUNSE);
        //send notification to customer
        LOG.debug("Sending refund reject notification to user");
//        rejectNotication.sendNotification(apply);//留空
    }

    /**
     * 获取退票单并判断该退票申请单是否处于一审状态
     */
//    @Source(name = "004031004")
    private RefundApplyDto loadApplyAndCheckStatus(long applyId) {
        // 退票单
        RefundApplyDto refundApply = refundApplyDao.getRefundApplyDto(applyId);

        if (refundApply == null) {
            throw new ServiceException("004031004001", "退票申请单" + "[ID:" + applyId + "]不存在！");
        }
        if (!(RefundStatus.FIRST_AUDIT_EXCEPTION.getCode().equals(refundApply.getStatus())
                || RefundStatus.WAIT_FIRST_AUDIT.getCode().equals(refundApply.getStatus())
                || RefundStatus.SECOND_AUDIT_REFUSE.getCode().equals(refundApply.getStatus()))) {
            LOG.error("申请单" + "[ID:" + refundApply.getId() + "]不是处于一审状态，无法进行一审操作！");
            throw new ServiceException("004031004002", "申请单" + "[ID:" + refundApply.getId() + "]不是处于一审状态，无法进行一审操作！");
        }
        return refundApply;
    }

    protected void saveAudit(RefundApplyDto apply, BaseAuditReq auditReq, AuditStatus auditStatus) {
        // 退票审核
        RefundAuditDto audit = new RefundAuditDto();
        audit.setRefundApply(apply);
        audit.setAuditorId(auditReq.getSessionUserId());
        audit.setAuditorName(auditReq.getAuditName());
        audit.setAuditDate(new Date());
        audit.setAuditStatus(auditStatus.getCode());
        audit.setAuditDesc(auditReq.getRemark());
        audit.setAuditPhase(String.valueOf(AuditPhase.FIRST_ADUIT.getCode()));
        refundApplyDao.insertRefundAudit(audit);
        apply.addRefundAudit(audit);
    }

    protected void updateRefundTax(FirstAuditReq auditReq, RefundTktDto refundTkt) {
        Set<RefundTaxDto> taxs = refundTkt.getRefundTaxes();
        List<TaxReq> taxReqs = auditReq.getTaxReqs();
        for (TaxReq req : taxReqs) {
            long reqTaxId = req.getId();
            boolean flag = false;
            for (RefundTaxDto tax : taxs) {
                long taxId = tax.getId();
                if (reqTaxId == taxId) {
                    checkTax(tax, req);
                    flag = true;
                    BigDecimal refundTax = req.getRefundValue();
                    LOG.info("Tax {}:{} is setting to {}, original value is {}", tax.getId(), tax.getTaxType(), refundTax, tax.getOrignalValue());
                    tax.setRefundValue(refundTax);
                    refundTaxDao.updateRefundTaxDto(tax);
                    break;
                }
            }
            if (!flag) {
                throw new ServiceException("004031003001", "Request tax id " + reqTaxId + " not found");
            }
        }
    }

    private void checkSegment(RefundTktDto refundTkt, RefundSegDto seg, SegReq req) {
        String originalRule = seg.getRefundRule();
        BigDecimal refundRule = req.getRealRefundRule();
        LOG.info("Original refund rule is {}, and the rule set to {}", originalRule, refundRule);
        BigDecimal refundFee = req.getRefundFee();
        BigDecimal refundAmt = req.getRefundAmount();
        BigDecimal segFacePrice;
        BigDecimal segFacePriceNew;
        if(null != seg.getFcnyDiffRes()){
            segFacePrice = seg.getTicketPrice().add(seg.getFcnyDiffRes());
            segFacePriceNew=seg.getTicketPrice().add(seg.getFcnyDiffRes());
            if(req.getFcnyDiffRes()!=null){
                segFacePriceNew = seg.getTicketPrice().add(req.getFcnyDiffRes());
            }
        }else{
            segFacePrice = seg.getTicketPrice();
            segFacePriceNew=seg.getTicketPrice();
            if(req.getFcnyDiffRes()!=null){
                segFacePriceNew = seg.getTicketPrice().add(req.getFcnyDiffRes());
            }
        }

        // 剩余票价，应该是起飞后的航班才会有该值
        if (req.getLeftTktPrice() != null) {
            segFacePrice = req.getLeftTktPrice();
            segFacePriceNew= req.getLeftTktPrice();
            if(req.getFcnyDiffRes()!=null){
                segFacePriceNew = req.getLeftTktPrice().add(req.getFcnyDiffRes());
            }
        }

        final long segId = seg.getId();
        LOG.info("Segment {} face price is {}", segId, segFacePrice);
        BigDecimal rule;
        BigDecimal calFee;
        BigDecimal calAmt;
        BigDecimal calFeeNew;
        BigDecimal calAmtNew;
        BigDecimal calAmtNew2;

        if (originalRule.contains("%")) {
            if (refundRule.compareTo(BigDecimal.ZERO) < 0 || refundRule.compareTo(BigDecimal.valueOf(100L)) > 0) {
                LOG.error("Segment {} Invald refund rule percentage value {}", segId, refundRule);
                throw new ServiceException("004031005001", "Segment " + req.getSegId() + " refund rule invalid");
            }
            calFee = segFacePrice.multiply(refundRule).divide(new BigDecimal(100));
            calFeeNew = segFacePriceNew.multiply(refundRule).divide(new BigDecimal(100));
            String currency = refundTkt.getCurrency();
            calFee = CurrencyUtil.getProfitableValue(currency, calFee);
            calFeeNew = CurrencyUtil.getProfitableValue(currency, calFeeNew);
        } else {
            rule = refundRule;
            if (rule.compareTo(BigDecimal.ZERO) < 0 || rule.compareTo(segFacePrice) > 0) {
                LOG.error("Segment {} Invalid refund rule value {}", segId, rule);
                throw new ServiceException("004031005001", "Segment " + segId + " refund rule invalid");
            }
            calFee = rule;
            calFeeNew = rule;
        }
        calAmt = segFacePrice.subtract(calFee);
        calAmtNew = segFacePriceNew.subtract(calFeeNew);
        calAmtNew2 = segFacePriceNew.subtract(calFee);
        LOG.info("Segment {} request refund fee is {}, calculate calFee is {}", segId, refundFee, calFee);
        LOG.info("Segment {} request refund amount is {}, calculate calAmt is {}", segId, refundAmt, calAmt);
        LOG.info("Segment {} request refund calFeeNew is {}", segId, calFeeNew);
        LOG.info("Segment {} request refund segFacePrice is {}", segId, segFacePrice);
        LOG.info("Segment {} request refund segFacePriceNew is {}", segId, segFacePriceNew);
        LOG.info("Segment {} request refund calAmtNew is {}", segId, calAmtNew);
        LOG.info("Segment {} request refund calAmtNew2 is {}", segId, calAmtNew2);
//        if (refundFee.compareTo(calFee) != 0 || refundAmt.compareTo(calAmt) != 0) {
//            LOG.error("Segment {} request refund fee is {} and refund amount value {}", segId, refundFee, refundAmt);
//            throw new ServiceException("004031005003", "Segment " + segId + " refund fee or refund amount invalid");
//        }
        if ((refundFee.compareTo(calFeeNew) != 0 && refundFee.compareTo(calFee) != 0)
                || (refundAmt.compareTo(calAmt) != 0 && refundAmt.compareTo(calAmtNew) != 0 && refundAmt.compareTo(calAmtNew2) != 0)) {
            LOG.error("Segment {} request refund fee is {} and refund amount value {}", segId, refundFee, refundAmt);
            //RefundTkt refundTkt, RefundSeg seg, SegReq req
            LOG.error("refundTkt.getApplyNo {} RefundSegId is {} and SegReq {}", refundTkt.getApplyNo(), seg.getId(), GsonUtil.toJson(req));
            throw new ServiceException("004031005003", "Segment " + segId + " refund fee or refund amount invalid");
        }
    }

    private void checkTax(RefundTaxDto tax, TaxReq req) {
        BigDecimal originalValue = tax.getOrignalValue();
        BigDecimal refundValue = req.getRefundValue();
        if (refundValue.compareTo(BigDecimal.ZERO) < 0 || refundValue.compareTo(originalValue) > 0) {
            LOG.error("Tax {}{} setting invalid value {}, the original value is {}", tax.getId(), tax.getTaxType(), refundValue, originalValue);
            throw new ServiceException("004031006001", "Tax " + tax.getTaxType() + " setting invalid value " + refundValue + ", the original value is " + originalValue);
        }
    }

    /**
     * 国际航线票, 航段更新
     *
     * @param auditReq
     * @param refundTkt
     */
//    @Source(name = "004031002")
    protected void updateRefundSegment4Inter(FirstAuditReq auditReq, RefundTktDto refundTkt) {
        Set<RefundSegDto> segs = refundTkt.getRefundSegs();
        for (RefundSegDto seg : segs) {
            //国际航线票
            String realRefundRule = String.valueOf(auditReq.getRealRefundRule());
            if (seg.getRefundRule().contains("%")) {
                realRefundRule = realRefundRule + "%";
            }
            LOG.info("International segment {} setting refund rule value:{}", seg.getId(), realRefundRule);
            seg.setRealRefundRule(realRefundRule);
            refundSegDao.updateRefundSegDto(seg);
        }
    }

    /**
     * 国内航线票, 航段更新
     *
     * @param auditReq
     * @param refundTkt
     */
//    @Source(name = "004031002")
    protected void updateRefundSegment4Dome(FirstAuditReq auditReq, RefundTktDto refundTkt) {
        Set<RefundSegDto> segs = refundTkt.getRefundSegs();
        List<SegReq> segReqs = auditReq.getSegReqs();
        for (SegReq req : segReqs) {
            long reqSegId = req.getSegId();
            boolean flag = false;
            for (RefundSegDto seg : segs) {
                long segId = seg.getId();
                if (reqSegId == segId) {
                    flag = true;
                    checkSegment(refundTkt, seg, req);

                    String realRefundRule = String.valueOf(req.getRealRefundRule());
                    if (seg.getRefundRule().contains("%")) {
                        realRefundRule = realRefundRule + "%";
                    }
                    LOG.info("Segment {} setting refund rule value:{}", segId, realRefundRule);
                    seg.setRealRefundRule(realRefundRule);

                    BigDecimal refundFee = req.getRefundFee();
                    seg.setRefundFee(refundFee);
                    BigDecimal refundAmt = req.getRefundAmount();
                    seg.setRefundAmount(refundAmt);
                    Long refundPoints = req.getRefundPoints();
                    seg.setRefundPoints(refundPoints);
                    seg.setFcnyDiffRes(req.getFcnyDiffRes());
                    LOG.info("Demostic segment {} setting value refund rule:{}, refund amount exclude tax:{}, refund fee:{}, refund points:{}", segId, realRefundRule, refundAmt, refundFee, refundPoints);
                    refundSegDao.updateRefundSegDto(seg);
                    break;
                }
            }
            if (!flag) {
                throw new ServiceException("004031002001", "Request segment id " + reqSegId + " not found");
            }

        }
    }

    protected void updateRefundTicket(FirstAuditReq auditReq, RefundTktDto refundTkt) {
        Set<RefundTaxDto> taxs = refundTkt.getRefundTaxes();
        BigDecimal tktRefundTax = BigDecimal.ZERO;
        for (RefundTaxDto tax : taxs) {
            BigDecimal segTax = tax.getRefundValue();
            if (segTax != null) {
                tktRefundTax = tktRefundTax.add(segTax);
            }
        }
        checkRefundTax(tktRefundTax, auditReq);
        LOG.info("Ticket total refund tax {}", tktRefundTax);
        refundTkt.setRefundTax(tktRefundTax);

        BigDecimal tktRefundFee = BigDecimal.ZERO;
        BigDecimal tktRefundAmt = BigDecimal.ZERO;
        Long tktRefundPoints = 0L;
        //国际航线
        if (refundTkt.isInterAirline()) {
            tktRefundFee = auditReq.getRefundFee();
            tktRefundAmt = auditReq.getRefundAmount();
            tktRefundPoints = auditReq.getRefundPoints();
        } else {
            Set<RefundSegDto> segs = refundTkt.getRefundSegs();
            for (RefundSegDto seg : segs) {
                BigDecimal segRefundFee = seg.getRefundFee();
                if (segRefundFee != null) {
                    tktRefundFee = tktRefundFee.add(segRefundFee);
                }
                BigDecimal segRefundAmt = seg.getRefundAmount();
                if (segRefundAmt != null) {
                    tktRefundAmt = tktRefundAmt.add(segRefundAmt);
                }
                Long segRefundPoints = seg.getRefundPoints();
                if (segRefundPoints != null) {
                    tktRefundPoints += segRefundPoints;
                }
            }
        }

        BigDecimal refundResGap = auditReq.getRefundResGap();
        if (refundResGap != null && refundResGap.compareTo(BigDecimal.ZERO) > 0) {
            LOG.info("Refund reschedule gap {}", refundResGap);
            refundTkt.setRefundResGap(refundResGap);
        }
        LOG.info("Ticket total refund fee {}", tktRefundFee);
        refundTkt.setRefundFee(tktRefundFee);

        LOG.info("Ticket total refund amount (exclude tax) {}", tktRefundAmt);
        refundTkt.setRefundAmt(tktRefundAmt);

        LOG.info("Ticket total refund points {}", tktRefundPoints);
        refundTkt.setRefundPoints(tktRefundPoints);

        BigDecimal leftTktPrice = auditReq.getLeftTktPrice();
        if (refundTkt.isDomesticAirline() && refundTkt.getPromoteAmt() != null) {
            List<SegReq> segReqs = auditReq.getSegReqs();
            BigDecimal segLeftTktPrice = BigDecimal.ZERO;
            for (SegReq segReq : segReqs) {
                BigDecimal segPrice = segReq.getLeftTktPrice();
                if (segPrice == null) {
                    LOG.error("Invalid segment left ticket price {}", segPrice);
                    throw new IllegalArgumentException("Invalid segment left ticket price " + segPrice);
                }
                LOG.info("Ticket {} segment {} set left ticket price to {}", refundTkt.getTicketNo(), segReq.getSegId(), segPrice);
                segLeftTktPrice = segLeftTktPrice.add(segPrice);
            }
            leftTktPrice = segLeftTktPrice;
        }
        if (((refundTkt.isPartailRefund() && refundTkt.isInterAirline()) || refundTkt.getPromoteAmt() != null)) {
            if (leftTktPrice != null && leftTktPrice.compareTo(BigDecimal.ZERO) >= 0) {
                LOG.info("Setting partial refund left ticket price to {}", leftTktPrice);
                refundTkt.setLeftTktPrice(leftTktPrice);
            } else {
                LOG.error("Invalid left ticket price {}", leftTktPrice);
                throw new IllegalArgumentException("Invalid feft ticket price " + leftTktPrice);
            }
        }
        refundTktDao.updateTktDtoById(refundTkt);
    }

    protected void updateRefundApply(FirstAuditReq auditReq, RefundApplyDto apply) {

        RefundTktDto refundTkt = apply.getRefundTkt();
        // 总的票面退票金额
        BigDecimal refundAmt = refundTkt.getRefundAmt();
        // 总的税费
        BigDecimal totalTax = refundTkt.getRefundTax();

        //改期票, 需要退税费差价和票面差价
        if (refundTkt.isReschedule()) {
            //前端页面refundAmt已经加了改期票面差，再次加改期票面差，就重复计算了。
//            BigDecimal facePriceGap = refundTkt.getRescheduleGap();
//            BigDecimal refundResGap = auditReq.getRefundResGap();
//            if (facePriceGap != null) {
//                if (refundResGap == null || facePriceGap.compareTo(refundResGap) < 0) {
//                    LOG.error("Refund rechedule gap is empty or greater than total reschedule gap, total:{} vs refund:{}", facePriceGap, refundResGap);
//                    throw new IllegalStateException("Refund rechedule gap is empty or greater than total reschedule gap");
//                }
//                refundAmt = refundAmt.add(refundResGap);//加上退还的改期票面差额
//            }
            BigDecimal taxGap = refundTkt.getRescheduleTax();
            if (taxGap != null) {
                totalTax = totalTax.add(taxGap);
            }
        }

        //总手续费(所有航段上的手续费之和)
        BigDecimal totalRefundFee = refundTkt.getRefundFee();
        LOG.info("退票申请单 ID:{}的退票手续费为: {}", auditReq.getApplyId(), totalRefundFee);
        apply.setRefundFee(totalRefundFee);

        BigDecimal calTotalRefundAmt = refundAmt.add(totalTax);
        checkTotalRefundAmt(calTotalRefundAmt, auditReq);

        // 应退金额
        BigDecimal totalRefundAmt = auditReq.getApplyAmt();
        LOG.info("退票申请单 ID:{}的应退金额为:{}", auditReq.getApplyId(), totalRefundAmt);
        apply.setRefundAmount(totalRefundAmt);

        if (apply.getPointsType() != null) {
            Long totalRefundPoints = auditReq.getRefundPoints();
            LOG.info("退票申请单 ID:{}的应退积分为:{}", auditReq.getApplyId(), totalRefundPoints);
            apply.setRefundPoints(totalRefundPoints);
        }

        // 设置等待二审状态
        apply.setStatus(RefundStatusEnum.WAIT_SECOND_AUDIT.getCode());
        apply.setUpdatDate(new Date());
        // 退票原因
        if (auditReq.getReason() != null) {
            apply.setClaimReason(auditReq.getReason().getCode());
        }
        String actionTip = apply.getActionTip();
        if (actionTip != null && actionTip.contains(ModuleConstants.REFUND_PRICE_NOT_MATCH_EXCEPTION)) {
            apply.setActionTip(actionTip.replace(ModuleConstants.REFUND_PRICE_NOT_MATCH_EXCEPTION, ""));
        }
        refundApplyDao.updateRefundApply(apply);
    }

    private void checkTotalRefundAmt(BigDecimal totalRefundAmt, FirstAuditReq auditReq) {
        if (totalRefundAmt.compareTo(auditReq.getApplyAmt()) != 0) {
            LOG.error("Total refund amount is not match, calculated value: {} value in request: {} ", totalRefundAmt, auditReq.getApplyAmt());
            LOG.error("Total refund totalRefundAmt value: {} FirstAuditReq: {} ", totalRefundAmt, GsonUtil.toJson(auditReq));
            throw new ServiceException("004031003001", "Total refund amount value not match");
        }
    }

    private void checkRefundTax(BigDecimal tktRefundTax, FirstAuditReq auditReq) {
        if (tktRefundTax.compareTo(auditReq.getRefundTaxAmt()) != 0) {
            LOG.error("Ticket refund tax is not match, calculated value: {} value in request: {}", tktRefundTax, auditReq.getRefundTaxAmt());
            throw new ServiceException("004031003001", "Refund tax value not match");
        }
    }
}
